#!c:/usr/perl

##########################
#     OmnibusProxy       #
#    Krzysztof Ulman     #
# Politechnika Krakowska #
#      Kraków 2012       #
##########################

use strict;
use warnings;
use utf8;
use encoding 'utf8';

use Try::Tiny;

use AppConfig;
use ApplicationUtils;
use CollectDataManager;
use Database;
use NewContentManager;
use ProxyManager;
use ResultObject;
use Screen;
use SpiderController;
use StatementsManager;
use StatementsUtils;

#wywoluje sprawdzanie wszystkich proxy w bazie
our $OPTION_CHECK_PROXY = 'checkProxy';
#rozpoczyna zbieranie danych
our $OPTION_COLLECT_DATA = 'collectData';
#wczytuje frazy do pobrania
our $OPTION_LOAD_STATEMENTS = 'loadStatements';
#wczytuje frazy do pobrania i miesza je
our $OPTION_LOAD_STATEMENTS_AND_HASH_THEM = 'loadStatementsAndHashThem';
#rozwiazuje adresy nie wprost
our $OPTION_RESOLVE_NOT_DIRECT_URLS = 'resolveNotDirectUrls';
#robi unescape na wszystkich adresach
our $OPTION_UNESCAPE_URLS = 'unescapeUrls';
#przenosi dane miedzy tabelami
our $OPTION_MOVE_DATA = 'moveData';
#laczy tokeny z lematami
our $OPTION_JOIN_TOKENS_WITH_LEMATS = 'joinTokensWithLemats';

#wybrana opcja
#my $option = $OPTION_CHECK_PROXY;
my $option = $OPTION_COLLECT_DATA;
#my $option = $OPTION_LOAD_STATEMENTS;
#my $option = $OPTION_LOAD_STATEMENTS_AND_HASH_THEM;
#my $option = $OPTION_RESOLVE_NOT_DIRECT_URLS;
#my $option = $OPTION_UNESCAPE_URLS;
#my $option = $OPTION_MOVE_DATA;
#my $option = $OPTION_JOIN_TOKENS_WITH_LEMATS;
#my $option = 'null';

#identyfikator polaczenia do bazy danych
my $db;

#obiekt loggera
my $logger;

#czas rozpoczecia pracy aplikacji
our $startTime = time;

our $isAnotherInstance = 0;

#rozpoczecie pracy
if(before()) {

	if($option eq $OPTION_CHECK_PROXY) {
		checkProxy();
	} elsif($option eq $OPTION_COLLECT_DATA) {
		collectData();
	} elsif($option eq $OPTION_LOAD_STATEMENTS) {
		loadStatements();
	} elsif($option eq $OPTION_LOAD_STATEMENTS_AND_HASH_THEM) {
		loadStatementsAndHashThem();
	} elsif($option eq $OPTION_RESOLVE_NOT_DIRECT_URLS) {
		resolveNotDirectUtls();
	} elsif($option eq $OPTION_UNESCAPE_URLS) {
		unescapeUrls();
	} elsif($option eq $OPTION_MOVE_DATA) {
		moveData();
	} elsif($option eq $OPTION_JOIN_TOKENS_WITH_LEMATS) {
		joinTokensWithLemats();
	} else {
		Screen->printAndLogWarn('Opcja wybrana nieprawidlowo');
	}

}

#zakonczenie pracy
after();

############################
# funkcje
############################

#akcje wykonywane przed rozpoczeciem pracy
sub before {
	my $result;
	try {
		#zainicjowanie ziarna do losowania
		srand(time);
		
		#wczytanie ustawien z pliku
		AppConfig::initFromFile();
		
		#konfiguracja loggera
		$logger = loggerConfig();
		
		#polaczenie z baza
		Database->init(AppConfig->getProperty('PREFIX'),
			AppConfig->getProperty('DB_NAME'),
			AppConfig->getProperty('DB_USER'),
			AppConfig->getProperty('DB_PASSWORD'));
		$db = Database->new();
		$db->connect();
		
		#wczytanie ustawien z bazy
		AppConfig::initFromDb();
		
		#konfiguracja Screen'a
		Screen->init();
		Screen->initAdditionalLogging();
		
		if(ApplicationUtils->isAnyInstanceWorking()) {
			#sprawdzenie, czy czasami na tej bazie nie pracuje juz inna instancja programu
			$isAnotherInstance = 1;
			die('Uruchomiona inna instancja programu.');
		} else {
			#zaznaczenie poczatku pracy
			ApplicationUtils->setStateStart();
		}
		
		Screen->printAndLogInfo('----------------------------------------');
		Screen->printAndLogInfo('START programu ' . $db->getDbDateTime() . ' ' . time);
		
		$result = 1;
	} catch {
		my $e = $_;
		if($logger) {
			$logger->fatal($e);
		}
		print "$e\n";
		$result = 0;
	};
	
	return $result;
}

#akcje wykonywane po zakonczeniu pracy
sub after {
	#aktualizacja stanu
	if(!$isAnotherInstance) {
		ApplicationUtils->setStateStop();
		Screen->printAndLogInfo('KONIEC pracy programu ' . $db->getDbDateTime() . ' ' . time);
		Screen->printAndLogInfo('CZAS pracy programu ' . (time - $startTime)/60 . ' min.');
		my $statementsManaged = StatementsManager->getTotalStatementsManaged();
		Screen->printAndLogInfo('OBSLUZONO fraz: ' . $statementsManaged . ' sztuk');
		my $requestNumber = SpiderController->getTotalDownloaded();
		Screen->printAndLogInfo('WYKONANYCH REQUESTOW: ' . $requestNumber . ' sztuk');
	}
	#rozlaczenie bazy danych
	$db->disconnect();
}

#konfiguracja Log4Perl
sub loggerConfig {
	Log::Log4perl->init(AppConfig->getProperty('LOGS_CONF_FILENAME'));	
	my $log = Log::Log4perl->get_logger("Main");
	return $log;
}

#sprawdza czy proxy dzialaja
sub checkProxy {
	Screen->printAndLogDebug('ROZPOCZETO testowanie proxy');
	my $proxyManager = ProxyManager->new();
	$proxyManager->testProxies();
	Screen->printAndLogDebug('ZAKONCZONO testowanie proxy');
}

#rozpoczyna zbieranie danych z wyszukiwarek
sub collectData {
	Screen->printAndLogDebug('ROZPOCZETO zbieranie danych');
	my $collectDataManager = CollectDataManager->new();
	if($collectDataManager->prepare()) {
		$collectDataManager->doWork();
	}
	Screen->printAndLogDebug('ZAKONCZONO zbieranie danych');
}

#wczutje frazy z pliku
sub loadStatements {
	my $fileName = shift;
	if(!$fileName) {
		$fileName = 'statements.txt';
	}
	try {
		open(FILE, $fileName) || die('Nie udalo sie otworzyc pliku ' . $fileName);
		Screen->printAndLogInfo('Rozpoczynam wczytywanie fraz z pliku: ' . $fileName);
		while(my $line = <FILE>) {
			chomp($line);
			if($line ne '' && $line !~ /^--/ && $line !~ /^\/\// && $line !~ /^#/) {
				StatementsUtils->insertStatementIntoDb($line);
			} else {
				Screen->printAndLogDebug('Pominieto: ' . $line);
			}
		}
		close(FILE);
	} catch {
		my $e = $_;
		Screen->printAndLogError('Nieudane wczytywanie: ' . $e);
	};
}

#wczutje frazy z pliku i losuje ich kolejnosc
sub loadStatementsAndHashThem {
	my $fileName = shift;
	if(!$fileName) {
		$fileName = 'statements.txt';
	}
	try {
		open(FILE, $fileName) || die('Nie udalo sie otworzyc pliku ' . $fileName);
		Screen->printAndLogInfo('Rozpoczynam wczytywanie fraz z pliku: ' . $fileName);
		my @srcLines;
		while(my $line = <FILE>) {
			chomp($line);
			if($line ne '' && $line !~ /^--/ && $line !~ /^\/\// && $line !~ /^#/) {
				push(@srcLines, $line);
			} else {
				Screen->printAndLogDebug('Pominieto: ' . $line);
			}
		}
		close(FILE);
		
		my @dstLines;
		while(@srcLines) {
			push(@dstLines, splice(@srcLines,rand(@srcLines), 1));
		}
		my $addedCounter = 0;
		my $existCounter = 0;
		foreach my $line (@dstLines) {
			if(StatementsUtils->insertStatementIntoDb($line)) {
				$addedCounter++;
			} else {
				$existCounter++;
			}
		}
		Screen->printAndLogInfo('Dodatno ' . $addedCounter . ' nowych fraz. Ponad to istnialo juz ' . $existCounter);
	} catch {
		my $e = $_;
		Screen->printAndLogError('Nieudane wczytywanie: ' . $e);
	};
}

#rozwiazuje adresy URL
sub resolveNotDirectUtls {
	my $contentManager = NewContentManager->new('AltaVista');
#	$db->executeQuery("SELECT addr_id, url FROM \"OMNIBUS_url_addrs\" WHERE url like 'search.yahoo.com%';");
	$db->executeQuery("SELECT addr_id, url FROM \"OMNIBUS_url_addrs\" WHERE url like '/url?q=%' OR url like '/url?url=%';");
	while((my $addrId, my $url) = $db->getRow()) {
		print "$url\n";
#		my $newUrl = $contentManager->resolveNotDirectUrl($url, '^search\.yahoo\.com');
		my $newUrl = $contentManager->resolveNotDirectUrl($url, '^\/url\?');
		$newUrl = ResultObject->manageUrl($newUrl);
		print "$newUrl\n\n";
		if($url ne $newUrl) {
			#sprawdzenie czy juz nie ma
			my $db3 = Database->new();
			$db3->executeQuery("SELECT count(*) as liczba FROM \"OMNIBUS_url_addrs\" WHERE url='$newUrl';");
			my $liczba = $db3->getRow();
			if($liczba == 1) {
				$db3->executeQuery("SELECT addr_id FROM \"OMNIBUS_url_addrs\" WHERE url='$newUrl';");
				my $existAddrId = $db3->getRow();
				#wszystkie urle podpiete do tego przepinamy pod ten istniejacy
				$db3->executeQuery("UPDATE \"OMNIBUS_urls\" SET addr_id=$existAddrId WHERE addr_id=$addrId;");
				#a ten usuwamy z bazy
				$db3->executeQuery("DELETE FROM \"OMNIBUS_url_addrs\" WHERE addr_id=$addrId;");
			} else {
				#aktualizacja
				my $db2 = Database->new();
				$db2->executeQuery("UPDATE \"OMNIBUS_url_addrs\" SET url='$newUrl' WHERE addr_id=$addrId;");
			}		
		}
	}		
}

#unescape URL
sub unescapeUrls { #www.emebel.pl/szukaj/sp%C3%B3%C5%82ki
	die('zeby nie wywolac przez przypadek');
	$db->executeQuery("SELECT addr_id, url FROM \"OMNIBUS_url_addrs\";");
	while((my $addrId, my $url) = $db->getRow()) {
		my $newUrl = ResultObject->manageUrl($url);
		if($url ne $newUrl) {
			print "$url\n";
			print "$newUrl\n\n";
			#sprawdzenie czy juz nie ma
			my $db3 = Database->new();
			$db3->executeQuery("SELECT count(*) as liczba FROM \"OMNIBUS_url_addrs\" WHERE url='$newUrl';");
			my $liczba = $db3->getRow();
			if($liczba == 1) {
				$db3->executeQuery("SELECT addr_id FROM \"OMNIBUS_url_addrs\" WHERE url='$newUrl';");
				my $existAddrId = $db3->getRow();
				#wszystkie urle podpiete do tego przepinamy pod ten istniejacy
				$db3->executeQuery("UPDATE \"OMNIBUS_urls\" SET addr_id=$existAddrId WHERE addr_id=$addrId;");
				#a ten usuwamy z bazy
				$db3->executeQuery("DELETE FROM \"OMNIBUS_url_addrs\" WHERE addr_id=$addrId;");
			} else {
				#aktualizacja
				my $db2 = Database->new();
				$db2->executeQuery("UPDATE \"OMNIBUS_url_addrs\" SET url='$newUrl' WHERE addr_id=$addrId;");
			}
		}
	}		
}

sub moveData {
	die('przypadkowe uruchomienie');
	$db->executeQuery("SELECT statement FROM \"OMNIBUS_statements\" WHERE is_new=1;");
	my $i = 0;
	while((my $statement) = $db->getRow()) {
		print ++$i . ") $statement\n";
		my $db2 = Database->new();
		$db2->executeQuery("INSERT INTO \"OMNIBUS_new_statements\"(statement, insert_timestamp) VALUES('$statement', 'NOW()');");
		
	}
}

sub joinTokensWithLemats {
	die('pzypadkowe uruchomienie');
	$db->executeQuery("SELECT token_id, token FROM \"OMNIBUS_tokens\";");
	while((my $tokenId, my $token) = $db->getRow()) {
		print "$token \n";
		my $db2 = Database->new();
		my $lematWordIdsRef = $db2->getLematWordIds($token);
		my @lematWordIds = @{$lematWordIdsRef};
		if(scalar(@lematWordIds) > 0) {
			foreach my $lematWordId (@lematWordIds) {
				$db2->insertTokensLemats($tokenId, $lematWordId);
			}
		}
	}
}